<?php
/**
 * Created by PhpStorm.
 * User: whwyy
 * Date: 2018/4/24 0024
 * Time: 17:15
 */

use BeReborn\Base\Authorize;
use BeReborn\Base\Platform;
use BeReborn\Database\ActiveRecord;
use BeReborn\Database\Collection;
use BeReborn\Error\RestfulHandler;
use BeReborn\Exception\AuthException;
use BeReborn\Exception\ComponentException;
use BeReborn\Http\Request;
use BeReborn\Service\Common\ServerWebSocket;
use BeReborn\Task\Task;
use BeReborn\Web\Application;
use BeReborn\Route\Router;
use BeReborn\Di\Container;
use Swoole\Http\Response as SResponse;
use Swoole\WebSocket\Server;
use BeReborn\Service\Http\Server as HServer;
use Swoole\Coroutine\System;

defined('APP_PATH') or define('APP_PATH', realpath(__DIR__ . '/../../../..'));
defined('DISPLAY_ERRORS') or define('DISPLAY_ERRORS', true);
defined('IMG_TYPES') or define('IMG_TYPES', [
    IMAGETYPE_GIF => '.gif',
    IMAGETYPE_JPEG => '.jpeg',
    IMAGETYPE_PNG => '.png',
    IMAGETYPE_SWF => '.swf',
    IMAGETYPE_PSD => '.psd',
    IMAGETYPE_BMP => '.bmp',
    IMAGETYPE_TIFF_II => '.tif_ii',
    IMAGETYPE_TIFF_MM => '.tif_mm',
    IMAGETYPE_JPC => '.jpc',
    IMAGETYPE_JP2 => '.jp2',
    IMAGETYPE_JPX => '.jpx',
    IMAGETYPE_JB2 => '.jb2',
    IMAGETYPE_SWC => '.swc',
    IMAGETYPE_IFF => '.iff',
    IMAGETYPE_WBMP => '.wbmp',
    IMAGETYPE_XBM => '.xbm',
    IMAGETYPE_ICO => '.ico',
    IMAGETYPE_WEBP => '.webp',
]);

class BeReborn
{

    /** @var Application */
    public static $app;

    /** @var Router */
    public static $router;

    /** @var Container */
    public static $container;

    private static $isStart = false;

    /** @var Platform */
    private static $_platform;

    /**
     * @param $value
     * @return mixed
     */
    public static function setIsStart($value)
    {
        return static::$isStart = $value;
    }

    /**
     * @return bool
     */
    public static function isStart()
    {
        return static::$isStart;
    }

    /**
     * @return mixed
     */
    public static function getFirstLocal()
    {
        return static::$app->getFirstLocal();
    }


    /**
     * @return Request
     */
    public static function getRequest()
    {
        return BeReborn::getApp('request');
    }


    /**
     * @return Router
     */
    public static function getRouter()
    {
        return BeReborn::getApp('router');
    }

    /**
     * @return Redis
     * @throws Exception
     */
    public static function getRedis()
    {
        return static::$app->getRedis();
    }

    /**
     * @param $pid
     * @throws Exception
     */
    public static function setProcessId($pid)
    {
        $runPath = \BeReborn::getRuntimePath('workerIds');
        if (!$pid) {
            return;
        }
        file_put_contents($runPath . '/' . $pid, $pid);
    }

    /**
     * @param $pid
     * @throws Exception
     */
    public static function clearProcessId($pid)
    {
        $runPath = \BeReborn::getRuntimePath('workerIds');
        if (!file_exists($runPath . '/' . $pid)) {
            return;
        }
        @unlink($runPath . '/' . $pid);
    }

    /**
     * @throws Exception
     */
    public static function clearAll()
    {
        $runPath = \BeReborn::getRuntimePath('workerIds');
        foreach (glob($runPath . '/*') as $item) {
            if (!file_exists($item)) {
                continue;
            }
            @unlink($item);
        }
    }

    /**
     * @return Application
     */
    public static function getApplication()
    {
        return static::$app;
    }

    /**
     * @param $name
     * @return mixed
     * @throws
     */
    public static function getApp($name)
    {
        return static::$app->get($name);
    }


    /**
     * @return HServer
     */
    public static function getServer(): HServer
    {
        return static::getApp('socket');
    }


    /**
     * @throws Exception
     */
    public static function getSockID()
    {
        $val = static::getRuntimePath() . '/socket.sock';
        if (!file_exists($val)) {
            return null;
        }
        return file_get_contents($val);
    }

    /**
     * @param $tmp
     * @return string
     */
    public static function rename($tmp)
    {
        $hash = md5_file($tmp['tmp_name']);

        $later = '.' . exif_imagetype($tmp['tmp_name']);

        $match = '/(\w{12})(\w{5})(\w{9})(\w{6})/';
        $tmp = preg_replace($match, '$1-$2-$3-$4', $hash);

        return strtoupper($tmp) . $later;
    }


    /**
     * @return mixed
     * @throws AuthException
     * @throws ComponentException
     */
    public static function getCurrentOnlineUser()
    {
        return static::getAuthorize()->getCurrentOnlineUser();
    }

    /**
     * @return Authorize
     * @throws ComponentException
     */
    public static function getAuthorize()
    {
        return static::$app->getAuth();
    }

    /**
     * @param $millisecond -- 毫秒
     * @param $callback
     * @return int|false
     */
    public static function after($millisecond, \Closure $callback)
    {
        return \Swoole\Timer::after($millisecond, $callback);
    }

    /**
     * @return bool
     */
    public static function inCoroutine()
    {
        try {
            return Swoole\Coroutine::getCid() > 0;
        } catch (Throwable $exception) {
            return false;
        }
    }

    /**
     * @param $millisecond
     * @param $callback
     * @return int|false
     */
    public static function tick($millisecond, \Closure $callback)
    {
        return \Swoole\Timer::tick($millisecond, $callback);
    }

    /**
     * @param $object
     * @param $config
     *
     * @return mixed
     */
    public static function configure($object, $config)
    {
        foreach ($config as $key => $val) {
            $object->$key = $val;
        }
        return $object;
    }

    /**
     * @param       $className
     * @param array $construct
     *
     * @return mixed|object
     * @throws
     */
    public static function createObject($className, $construct = [])
    {
        if (is_string($className)) {
            return static::$container->get($className, $construct);
        } else if (is_array($className)) {
            if (!isset($className['class']) || empty($className['class'])) {
                throw new Exception('Object configuration must be an array containing a "class" element.');
            }
            $class = $className['class'];
            unset($className['class']);
            return static::$container->get($class, $construct, $className);
        } else if (is_callable($className, TRUE)) {
            return call_user_func($className, $construct);
        } else {
            throw new Exception('Unsupported configuration type: ' . gettype($className));
        }
    }

    /**
     * @param string $event
     * @param array $fds
     * @param null $data
     * @throws Exception
     */
    public static function push(string $event, array $fds, $data = NULL)
    {
        $message = self::param($event, $data);

        /** @var \BeReborn\Service\Http\Server $service */
        $service = BeReborn::getApp('socket');
        if (!$service->enableWebSocket) {
            return;
        }

        $service = $service->getServer();
        foreach ($fds as $fd) {
            if (!$service->exist($fd)) {
                continue;
            }
            $service->push($fd, $message);
        }
    }

    /**
     * @param string $event
     * @param null $data
     * @return string
     * 构造推送内容
     * @throws Exception
     */
    public static function param(string $event, $data = NULL)
    {
        if (is_object($data)) {
            if ($data instanceof ActiveRecord || $data instanceof Collection) {
                $data = $data->toArray();
            } else {
                $data = get_object_vars($data);
            }
        }
        if (!is_array($data)) $data = ['data' => $data];
        return json_encode(array_merge(['callback' => $event], $data));
    }

    /**
     * @param $command
     * @param array $param
     * @return mixed
     */
    public static function command($command, array $param = [])
    {
        $_TMP = [APP_PATH . '/artisan', $command];
        foreach ($param as $key => $val) {
            if (is_array($val) || is_object($val)) {
                continue;
            }
            $_TMP[] = $key . '=' . $val;
        }
        $data = shell_exec(PHP_BINDIR . '/php ' . implode(' ', $_TMP));
        return trim($data);
    }

    /**
     * @return RestfulHandler
     */
    public static function getLogger()
    {
        return BeReborn::getApp('error');
    }

    /**
     * @param Task $task
     * @param int|null $work_id
     * @return mixed
     */
    public static function async(Task $task, int $work_id = null)
    {
        if (!static::hasApp('socket')) {
            return false;
        }

        /** @var \BeReborn\Service\Http\Server $server */
        $server = static::getApp('socket');
        if (empty($work_id)) {
            $work_id = $server->getRandWorker();
        }

        $format = serialize($task);
        $socket = $server->getServer();
        if (!$socket) {
            return false;
        }

        return $socket->task($format, $work_id);
    }

    /**
     * @param $name
     * @return bool
     */
    public static function hasApp($name)
    {
        return static::$app->has($name);
    }


    /**
     * @param $name
     * @param $callback
     * @param $param
     * @throws
     */
    public static function on($name, $callback, $param = null)
    {
        $event = static::getApp('event');
        $event->on($name, $callback, $param);
    }


    /**
     * @param $name
     * @param $callback
     * @param bool $isRemove
     */
    public static function trigger($name, $callback, $isRemove = false)
    {
        $event = static::getApp('event');
        $event->try($name, $callback, $isRemove);
    }


    /**
     * @param string $path
     * @return string
     * @throws Exception
     */
    public static function getRuntimePath($path = '')
    {
        $_path = '/';
        $basePath = BeReborn::$app->runtimePath;
        if (empty($path)) {
            return realpath($basePath);
        }
        $explode = array_filter(explode('/', $path));
        foreach ($explode as $value) {
            $_path .= $value . '/';
            if (!is_dir($basePath . $_path)) {
                mkdir($basePath . $_path);
            }
            if (!is_dir($basePath . $_path)) {
                throw new Exception('System error, directory ' . $basePath . $_path . ' is not writable');
            }
        }
        return realpath($basePath . $_path);
    }

    /**
     * @param array $param
     * @return stdClass
     */
    public static function createAnonymous(array $param)
    {
        $class = new stdClass();
        foreach ($param as $key => $value) {
            $class->{$key} = $value;
        }

        return $class;
    }

    /**
     * @return Server
     */
    public static function getWebSocket()
    {
        /** @var ServerWebSocket $socket */
        $socket = static::getApp('socket');
        return $socket->getServer();
    }

    /**
     * @param $response
     * @return mixed
     */
    public static function exists($response)
    {
        if (!($response instanceof SResponse)) {
            return false;
        }
        return true;
    }


    /**
     * @return Platform
     */
    public static function getPlatform()
    {
        return new Platform();
    }


    /**
     * @param $class
     * @param $ab
     * @return mixed|object
     * @throws Exception
     */
    public static function make($class, $ab)
    {
        if (is_object($class)) {
            return $class;
        }

        if (is_object($ab)) {
            return $ab;
        }

        if (empty($class)) {
            return static::createObject($ab);
        }

        return static::createObject($class);
    }

}


/**
 * @param string $server_name
 *
 * @return bool
 */
function process_exists($server_name = 'im server')
{
    $cmd = 'ps axu|grep "' . $server_name . '"|grep -v "grep"|wc -l';
    $ret = shell_exec("$cmd");
    return (bool)trim(rtrim($ret, "\r\n"));
}

BeReborn::$container = new Container();
